package de.tum.in.eist.im.client;

import java.util.Date;
import java.util.List;

import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.rpc.AsyncCallback;

import de.tum.in.eist.im.client.domain.Message;
import de.tum.in.eist.im.client.domain.NewMessagesChunk;
import de.tum.in.eist.im.client.domain.User;

public class Connector
{
    private UI ui;
    private ChatServiceAsync server = (ChatServiceAsync) GWT.create (ChatService.class);
    private boolean logged_in;

    private Timer pullTimer;
    private User user;
    private long clientLastTimestamp;

    public Connector (UI ui)
    {
        this.ui = ui;
        logged_in = false;

        pullTimer = new Timer ()
        {
            @Override
            public void run ()
            {
                if (logged_in)
                {
                    receiveMessages ();
                    getUserlist ();
                }
            }
        };
    }

    /**
     * Starts fetching of messages
     */
    private void start_updating ()
    {
        pullTimer.scheduleRepeating (1000);        
    }

    /**
     * Stops fetching of messages
     */
    private void stop_updating ()
    {
        pullTimer.cancel ();
    }

    /**
     * Logs the user in
     * 
     * @param name
     * @param password
     */
    public void login (String name, String password)
    {
        if (name.equals (""))
        {
            ui.setErrorMessage ("Name may not be empty");
        }
        else if (password.equals (""))
        {
            ui.setErrorMessage ("Password may not be empty");
        }
        else
        {
            AsyncCallback <Boolean> callback = new AsyncCallback <Boolean> ()
            {

                @Override
                public void onFailure (Throwable caught)
                {
                    // Communication Error
                    ui.setErrorMessage ("Error: " + caught.getMessage ());
                }

                @Override
                public void onSuccess (Boolean result)
                {
                    if (result)
                    {
                        logged_in = true;
                        ui.clearErrorMessage ();

                        ui.switchUI (UI.windows.CHAT);

                        clientLastTimestamp = 0;
                        
                        getUserlist ();
                        receiveMessages ();

                        start_updating ();
                    }
                    else
                    {
                        // Login failed
                        ui.setErrorMessage ("Error: Wrong Password or not registered yet");
                    }
                }
            };

            user = new User (name, password);
            server.login (user, callback);
        }
    }

    /**
     * Register the user
     * 
     * @param name
     * @param password
     */
    public void register (final String name, final String password)
    {
        // Password already tested on match
        user = new User (name, password);

        AsyncCallback <Void> callback = new AsyncCallback <Void> ()
        {

            @Override
            public void onFailure (Throwable caught)
            {
                // Communication Error
                ui.setErrorMessage ("Error: " + caught.getMessage ());
            }

            @Override
            public void onSuccess (Void result)
            {
                ui.clearErrorMessage ();
                // Register success? Try logging in...
                login (name, password);
            }
        };

        server.registerNewUser (user, callback);
    }

    /**
     * Logs the user out
     */
    public void logout ()
    {
        if (!logged_in)
        {
            return;
        }
        stop_updating ();

        AsyncCallback <Boolean> callback = new AsyncCallback <Boolean> ()
        {
            @Override
            public void onFailure (Throwable caught)
            {
                // Communication Error, assume user has lost connection
                ui.switchUI (UI.windows.LOGIN);
                ui.setErrorMessage ("Error: " + caught.getMessage ());
            }

            @Override
            public void onSuccess (Boolean result)
            {
                ui.switchUI (UI.windows.LOGIN);
                if (result)
                {
                    // Logout succesful
                    ui.clearErrorMessage ();
                }
                else
                {
                    // Logout failes, assume user wasn't logged in
                    ui.setErrorMessage ("Error: Logout failed");
                }
            }
        };

        logged_in = false;
        server.logout (user, callback);
    }

    /**
     * Pulls messages from the server
     */
    private void receiveMessages ()
    {
        server.getNewMessages (clientLastTimestamp, new AsyncCallback <NewMessagesChunk> ()
        {
            public void onSuccess (NewMessagesChunk msg)
            {
                clientLastTimestamp = msg.getTimestamp ();

                for (int i = 0; i < msg.getMessages ().size (); i++)
                {
                    ui.addMessage (msg.getMessages ().get (i));
                }
            }

            public void onFailure (Throwable caught)
            {
                // Communication Error, assume User lost connection and got
                // logged out
                ui.switchUI (UI.windows.LOGIN);
                ui.setErrorMessage ("Error: " + caught.getMessage ());
            }
        });
    }

    /**
     * Gets userlist from server
     */
    private void getUserlist ()
    {
        server.getLoggedInUsers (new AsyncCallback <List <User>> ()
        {
            public void onSuccess (List <User> lst)
            {
                ui.updateUserlist (lst);
            }

            @Override
            public void onFailure (Throwable caught)
            {
                // Communication Error, assume User lost connection and got
                // logged out
                ui.switchUI (UI.windows.LOGIN);
                ui.setErrorMessage ("Error: " + caught.getMessage ());
            }

        });
    }

    /**
     * Sends a message to the server
     * 
     * @param message
     */
    public void sendMessages (String message)
    {
        if (!logged_in)
        {
            ui.switchUI (UI.windows.LOGIN);
            ui.setErrorMessage ("Error: Unkonw Error");
            return;
        }
        Message msg = new Message (new Date().getTime (), message, user);
        
        server.sendMessage (msg, new AsyncCallback<Void> (){

            @Override
            public void onFailure (Throwable caught)
            {
                // Communication Error, assume User lost connection and got
                // logged out
                ui.switchUI (UI.windows.LOGIN);
                ui.setErrorMessage ("Error: " + caught.getMessage ());
            }

            @Override
            public void onSuccess (Void result)
            {
                // Message sent, nothing to do anymore
            }
            
        });
    }
}
